
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
import { __extends } from 'tslib';
/* global Float32Array */
// TODO Batch by color

import * as graphic from '../../util/graphic.js';
import { createSymbol } from '../../util/symbol.js';
import { getECData } from '../../util/innerStore.js';
var BOOST_SIZE_THRESHOLD = 4;

var LargeSymbolPathShape =
/** @class */
function () {
	function LargeSymbolPathShape() {}

	return LargeSymbolPathShape;
}();

var LargeSymbolPath =
/** @class */
function (_super) {
	__extends(LargeSymbolPath, _super);

	function LargeSymbolPath(opts) {
		var _this = _super.call(this, opts) || this;

		_this._off = 0;
		_this.hoverDataIdx = -1;
		return _this;
	}

	LargeSymbolPath.prototype.getDefaultShape = function () {
		return new LargeSymbolPathShape();
	};

	LargeSymbolPath.prototype.reset = function () {
		this.notClear = false;
		this._off = 0;
	};

	LargeSymbolPath.prototype.buildPath = function (path, shape) {
		var points = shape.points;
		var size = shape.size;
		var symbolProxy = this.symbolProxy;
		var symbolProxyShape = symbolProxy.shape;
		var ctx = path.getContext ? path.getContext() : path;
		var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD;
		var softClipShape = this.softClipShape;
		var i; // Do draw in afterBrush.

		if (canBoost) {
			this._ctx = ctx;
			return;
		}

		this._ctx = null;

		for (i = this._off; i < points.length;) {
			var x = points[i++];
			var y = points[i++];

			if (isNaN(x) || isNaN(y)) {
				continue;
			}

			if (softClipShape && !softClipShape.contain(x, y)) {
				continue;
			}

			symbolProxyShape.x = x - size[0] / 2;
			symbolProxyShape.y = y - size[1] / 2;
			symbolProxyShape.width = size[0];
			symbolProxyShape.height = size[1];
			symbolProxy.buildPath(path, symbolProxyShape, true);
		}

		if (this.incremental) {
			this._off = i;
			this.notClear = true;
		}
	};

	LargeSymbolPath.prototype.afterBrush = function () {
		var shape = this.shape;
		var points = shape.points;
		var size = shape.size;
		var ctx = this._ctx;
		var softClipShape = this.softClipShape;
		var i;

		if (!ctx) {
			return;
		} // PENDING If style or other canvas status changed?

		for (i = this._off; i < points.length;) {
			var x = points[i++];
			var y = points[i++];

			if (isNaN(x) || isNaN(y)) {
				continue;
			}

			if (softClipShape && !softClipShape.contain(x, y)) {
				continue;
			} // fillRect is faster than building a rect path and draw.
			// And it support light globalCompositeOperation.

			ctx.fillRect(x - size[0] / 2, y - size[1] / 2, size[0], size[1]);
		}

		if (this.incremental) {
			this._off = i;
			this.notClear = true;
		}
	};

	LargeSymbolPath.prototype.findDataIndex = function (x, y) {
		// TODO ???
		// Consider transform
		var shape = this.shape;
		var points = shape.points;
		var size = shape.size;
		var w = Math.max(size[0], 4);
		var h = Math.max(size[1], 4); // Not consider transform
		// Treat each element as a rect
		// top down traverse

		for (var idx = points.length / 2 - 1; idx >= 0; idx--) {
			var i = idx * 2;
			var x0 = points[i] - w / 2;
			var y0 = points[i + 1] - h / 2;

			if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) {
				return idx;
			}
		}

		return -1;
	};

	LargeSymbolPath.prototype.contain = function (x, y) {
		var localPos = this.transformCoordToLocal(x, y);
		var rect = this.getBoundingRect();
		x = localPos[0];
		y = localPos[1];

		if (rect.contain(x, y)) {
			// Cache found data index.
			var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y);
			return dataIdx >= 0;
		}

		this.hoverDataIdx = -1;
		return false;
	};

	LargeSymbolPath.prototype.getBoundingRect = function () {
		// Ignore stroke for large symbol draw.
		var rect = this._rect;

		if (!rect) {
			var shape = this.shape;
			var points = shape.points;
			var size = shape.size;
			var w = size[0];
			var h = size[1];
			var minX = Infinity;
			var minY = Infinity;
			var maxX = -Infinity;
			var maxY = -Infinity;

			for (var i = 0; i < points.length;) {
				var x = points[i++];
				var y = points[i++];
				minX = Math.min(x, minX);
				maxX = Math.max(x, maxX);
				minY = Math.min(y, minY);
				maxY = Math.max(y, maxY);
			}

			rect = this._rect = new graphic.BoundingRect(minX - w / 2, minY - h / 2, maxX - minX + w, maxY - minY + h);
		}

		return rect;
	};

	return LargeSymbolPath;
}(graphic.Path);

var LargeSymbolDraw =
/** @class */
function () {
	function LargeSymbolDraw() {
		this.group = new graphic.Group();
	}
	/**
   * Update symbols draw by new data
   */

	LargeSymbolDraw.prototype.updateData = function (data, opt) {
		this._clear();

		var symbolEl = this._create();

		symbolEl.setShape({
			points: data.getLayout('points')
		});

		this._setCommon(symbolEl, data, opt);
	};

	LargeSymbolDraw.prototype.updateLayout = function (data) {
		var points = data.getLayout('points');
		this.group.eachChild(function (child) {
			if (child.startIndex != null) {
				var len = (child.endIndex - child.startIndex) * 2;
				var byteOffset = child.startIndex * 4 * 2;
				points = new Float32Array(points.buffer, byteOffset, len);
			}

			child.setShape('points', points); // Reset draw cursor.

			child.reset();
		});
	};

	LargeSymbolDraw.prototype.incrementalPrepareUpdate = function (data) {
		this._clear();
	};

	LargeSymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {
		var lastAdded = this._newAdded[0];
		var points = data.getLayout('points');
		var oldPoints = lastAdded && lastAdded.shape.points; // Merging the exists. Each element has 1e4 points.
		// Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization)

		if (oldPoints && oldPoints.length < 2e4) {
			var oldLen = oldPoints.length;
			var newPoints = new Float32Array(oldLen + points.length); // Concat two array

			newPoints.set(oldPoints);
			newPoints.set(points, oldLen); // Update endIndex

			lastAdded.endIndex = taskParams.end;
			lastAdded.setShape({
				points: newPoints
			});
		} else {
			// Clear
			this._newAdded = [];

			var symbolEl = this._create();

			symbolEl.startIndex = taskParams.start;
			symbolEl.endIndex = taskParams.end;
			symbolEl.incremental = true;
			symbolEl.setShape({
				points: points
			});

			this._setCommon(symbolEl, data, opt);
		}
	};

	LargeSymbolDraw.prototype.eachRendered = function (cb) {
		this._newAdded[0] && cb(this._newAdded[0]);
	};

	LargeSymbolDraw.prototype._create = function () {
		var symbolEl = new LargeSymbolPath({
			cursor: 'default'
		});
		symbolEl.ignoreCoarsePointer = true;
		this.group.add(symbolEl);

		this._newAdded.push(symbolEl);

		return symbolEl;
	};

	LargeSymbolDraw.prototype._setCommon = function (symbolEl, data, opt) {
		var hostModel = data.hostModel;
		opt = opt || {};
		var size = data.getVisual('symbolSize');
		symbolEl.setShape('size', size instanceof Array ? size : [size, size]);
		symbolEl.softClipShape = opt.clipShape || null; // Create symbolProxy to build path for each data

		symbolEl.symbolProxy = createSymbol(data.getVisual('symbol'), 0, 0, 0, 0); // Use symbolProxy setColor method

		symbolEl.setColor = symbolEl.symbolProxy.setColor;
		var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD;
		symbolEl.useStyle( // Draw shadow when doing fillRect is extremely slow.
			hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color']));
		var globalStyle = data.getVisual('style');
		var visualColor = globalStyle && globalStyle.fill;

		if (visualColor) {
			symbolEl.setColor(visualColor);
		}

		var ecData = getECData(symbolEl); // Enable tooltip
		// PENDING May have performance issue when path is extremely large

		ecData.seriesIndex = hostModel.seriesIndex;
		symbolEl.on('mousemove', function (e) {
			ecData.dataIndex = null;
			var dataIndex = symbolEl.hoverDataIdx;

			if (dataIndex >= 0) {
				// Provide dataIndex for tooltip
				ecData.dataIndex = dataIndex + (symbolEl.startIndex || 0);
			}
		});
	};

	LargeSymbolDraw.prototype.remove = function () {
		this._clear();
	};

	LargeSymbolDraw.prototype._clear = function () {
		this._newAdded = [];
		this.group.removeAll();
	};

	return LargeSymbolDraw;
}();

export default LargeSymbolDraw;